package br.ufpe.cin.mac.middleware.connectors.concrete;

import java.awt.Frame;
import java.awt.List;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.Socket;
import java.net.UnknownHostException;

import br.ufpe.cin.mac.middleware.connectors.ConcreteConnector;
import br.ufpe.cin.mac.util.Constants;
import br.ufpe.cin.mac.util.Message;

public class RTPClient extends ConcreteConnector implements RTPClientMBean {

	public static final int RTP_PROTOCOL = 1;

	public static final int RTCP_PROTOCOL = 2;

	static int LOCAL_BUFFER_SIZE = 1024 * 1024;

	// for GOP with lost packet discard
	boolean bDiscard = false;

	boolean waitHeader = false;

	boolean bShowList = true;

	// for statistics
	int nPacketMissed = 0;

	int nPacketReceived = 0;

	int nGOPSize = 0;

	int nGOPCount = 0;

	int nGOP = 0;

	int nSeqNum = -1; // next expected sequence number

	int nTotalByte = 0;

	DatagramSocket socket = null;

	InetAddress netAdd = null;

	// windows and controls
	Frame frame;

	List listBox = null;

	// network config
	int protocol;

	String address;

	int port;

	// screen and file output
	BufferedOutputStream bos = null;

	BufferedOutputStream bos2 = null;

	PrintWriter logFile = null;

	// local buffering
	byte[] videoBuffer = null;

	int videoBufferSize = 0;

	byte[] seqHeaderPacket = null;

	// shared buffered with player
	// SharedBufferDataSource sbds;

	boolean bInitPlay = false;

	private Message msg;

	FileOutputStream fos;

	public RTPClient() {
		super(RTPClient.class.getCanonicalName());

	}

	/*
	 * public setRTPClient(String address, int port, int protocol,
	 * SharedBufferDataSource dSource) { this(address, port, protocol);
	 * this.sbds = dSource; }
	 */
	public void setRTPClient(String address, int port, int protocol) {
		// create an window for output
		if (protocol == RTCP_PROTOCOL) { // RTCP, should use odd number
			if ((port & 0x0001) == 0)
				port += 1;
			videoBuffer = null;
		} else { // RTP should use even number
			if ((port & 0x0001) != 0)
				port -= 1;
			videoBuffer = new byte[LOCAL_BUFFER_SIZE]; // 50k buffer
			videoBufferSize = 0;
		}

		this.address = address; // set address
		this.port = port; // set port
		this.protocol = protocol; // set protocol
	}

	public void connect() {
		try {
			netAdd = InetAddress.getByName(address);
		} catch (UnknownHostException e1) {
			System.out.println("Address: " + e1.getMessage());
			System.exit(1);
		}

		try {
			if (netAdd.isMulticastAddress()) {
				// System.out.println("Multicast address detected");
				socket = (DatagramSocket) new MulticastSocket(this.port);
				((MulticastSocket) socket).joinGroup(netAdd);
				System.out.println("Multicast Port: " + socket.getLocalPort());
			} else {
				// System.out.println("Unicast address detected");
				socket = new DatagramSocket(this.port);
				System.out.println("Unicast Port: " + socket.getLocalPort());
			}
		} catch (IOException e) {
			System.out.println("Multicast IO: " + e.getMessage());
			System.exit(1);
		}
	}

	public void run() {
		connect();

		byte buf[] = new byte[10000]; // 10K buffer
		try {
			fos = new FileOutputStream("rtp2.mpg");
			bos = new BufferedOutputStream(fos);
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (bDiscard)
			waitHeader = true;
		while (true) {
			try {

				int len;
				DatagramPacket recv;
				do {
					recv = new DatagramPacket(buf, buf.length);
					socket.receive(recv);
					len = recv.getLength();
				} while (len >= recv.getData().length);

				nPacketReceived++;
				// System.err.println(bos);
				bos.write(recv.getData());

				// Encapsula o Datagram num Message
				Message m = new Message(msg.getReceiver(), msg.getSender(),
						"Datagram");

				String e = new String(recv.getData());
				Object[] param = { e, recv.getLength() };
				// String tem = new String( recv.getData());
				// Object[] param = {tem, new Integer(recv.getLength()) };
				m.addInterceptor("Proxy", null);
				m.setType(Constants.RESPONSE);
				m.setParameter(param);

				m.setIndex(bytes2int(e.getBytes(), 2, 2));
				// System.err.println("Index: " + m.getIndex() + " OffSet: "
				// + recv.getData());

				// Coloca o dado na porta
				terS(m);

			} catch (IOException e) {
				System.err.println("Erro");
				System.out.println("Socket: " + e.getMessage());
			}
		}
	}

	@Override
	public void invC(Message msg) {
		// Seta o msg que ser� usado durante toda a conex�o
		this.msg = msg;
		buffer.put(msg.hashCode(), msg);
		sendNotification(msg.hashCode(), "invS");
	}

	@Override
	public Message invS(int hashMsg) {
		Message msg = buffer.get(new Integer(hashMsg));
		Object[] param = msg.getParameter();
		setRTPClient((String) param[0], (Integer) param[1], (Integer) param[2]);

		run();

		return null;
	}

	@Override
	public Message terC(int hashMsg) {
		// System.err.println(buffer.size());
		return (Message) this.buffer.remove(hashMsg);
	}

	@Override
	public void terS(Message msg) {

		this.buffer.put(msg.getIndex(), msg);
		sendNotification(msg.getIndex(), "terC");
	}

	public boolean beginSession() {
		// TODO Auto-generated method stub
		return false;
	}

	public void disconnect(Socket socket) throws Exception {
		// TODO Auto-generated method stub

	}

	@Override
	protected void msgManager(Message msg) {
		// TODO Auto-generated method stub

	}

	public RTPClient(String connectorName) {
		super(connectorName);
		// TODO Auto-generated constructor stub
	}

	private int bytes2int(byte[] b, int offset, int length) {
		int val = 0;

		for (int i = 0; i < length; i++) {
			val |= (((((int) (b[i + offset] >> 1) & 0x7f) << 1) | (int) (b[i
					+ offset] & 0x01)) << ((length - 1 - i) << 3));
		}
		return val;
	}

	@Override
	public void setHost(String host) {
		// TODO Auto-generated method stub

	}

}
